package org.exhibitj.operators;

import java.lang.reflect.Constructor;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.exhibitj.operators.matchers.EqualityMatcher;
import org.exhibitj.operators.matchers.Matcher;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public abstract class AbstractControlOperator<T extends Control, O extends ControlOperator> {
	protected T control;
	protected ShellOperator shell;
	
	@Before
	public void setUp() {
		openShellWith(widgetType());
	}

	/**
	 * Specify the type of widget to be tested.
	 * @return
	 */
	protected abstract Class<T> widgetType();
	
	private void openShellWith(final Class<T> type) {
		shell = new ShellOperator(new UIBuilder() {

			@Override
			public void create(Shell shell) {
				shell.setLocation(200, 200);
				shell.setSize(200, 100);
				
				try {
					Constructor<T> constructor = type.getConstructor(Composite.class, int.class);
					control = constructor.newInstance(shell, SWT.NONE);
					control.setLocation(25, 25);
					control.setSize(80, 30);
					control.pack();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		shell.open();
	}
	
	
	@Test(timeout=5000)
	public void disabledForUser() throws Exception {
		O operator = createOperator(); 
		manipulateWidget(new Runnable() {
			@Override
			public void run() {
				control.setEnabled(false);
			}
		});
		
		Assert.assertFalse(operator.isEnabled());
	}

	/**
	 * Create the operator which will be used to manipulate the 
	 * control.
	 * 
	 * @return
	 */
	protected abstract O createOperator();
	
	/**
	 * Used for updating or modifying the widget, intended before testing.
	 * 
	 * @param runner
	 */
	protected void manipulateWidget(Runnable runner) {
		SWTDisplayThread.syncExec(runner);
	}
	
	protected <E> Matcher<E> equalTo(E text) {
		return new EqualityMatcher<E>(text);
	}
	
	@After
	public void tearDown() {
		if (!shell.isDisposed()) {
			shell.close();
			shell = null;
		}
		control = null;
	}
}
